﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Linker.TaskCenter.Helpers;
using Linker.TaskCenter.Services;
using Linker.TaskCenter.Tasks.Dto;
using Linker.TaskCenter.Tasks.Entity;
using Linker.TaskCenter.Web.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus;

namespace Linker.TaskCenter.Tasks
{
    public class TaskAppService : TaskCenterAppService, ITaskAppService,
        ILocalEventHandler<EntityCreatedEventData<TaskEntity>>
    {
        private readonly ILogger<TaskAppService> _logger;
        private readonly CSRedisHelper _redisHelper;
        private readonly IRepository<TaskEntity, Guid> _repository;
        private readonly IRepository<ServiceEntity, Guid> _serviceRepository;

        public TaskAppService(IRepository<TaskEntity, Guid> repository,
            IRepository<ServiceEntity, Guid> serviceRepository,
            CSRedisHelper redisHelper,
            ILogger<TaskAppService> logger)
        {
            _repository = repository;
            _serviceRepository = serviceRepository;
            _logger = logger;
            _redisHelper = redisHelper;
        }

        [RemoteService(false)]
        public async Task HandleEventAsync(EntityCreatedEventData<TaskEntity> eventData)
        {
            var service = await _serviceRepository.GetAsync(eventData.Entity.ServiceId);
            var error = true;
            try
            {
                if (service != null)
                {
                    if (!string.IsNullOrEmpty(eventData.Entity.Request))
                    {
                        var request = new RequestMessageData
                        {
                            TaskId = eventData.Entity.Id,
                            Url = service.Url,
                            Parameters = JsonConvert.DeserializeObject(eventData.Entity.Request)
                        };
                        await _redisHelper.PushAsync(AppConsts.TaskQueueKey, request);
                        error = false;
                    }
                    else
                    {
                        _logger.LogError($"Task id {eventData.Entity.Id} request parameters error");
                        eventData.Entity.Error = "请求参数错误";
                    }
                }
            }
            catch (Exception e)
            {
                _logger.LogError(e.Message);
                eventData.Entity.Error = e.Message;
            }

            if (error)
            {
                eventData.Entity.ResponseResult = TaskResponseResultEnum.Error;
                eventData.Entity.Status = TaskStatusEnum.Done;
                await _repository.UpdateAsync(eventData.Entity);
            }
        }

        /// <summary>
        ///     获取服务
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<AjaxResponse<TaskDto>> Get(Guid id)
        {
            var entity = await _repository
                .Include(t => t.Service)
                .FirstOrDefaultAsync(t => t.Id == id);
            return new AjaxResponse<TaskDto>(ObjectMapper.Map<TaskEntity, TaskDto>(entity));
        }

        /// <summary>
        ///     创建服务
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<AjaxResponse<Guid>> Create(CreateTaskDto input)
        {
            var entity = ObjectMapper.Map<CreateTaskDto, TaskEntity>(input);
            await _repository.InsertAsync(entity);
            return new AjaxResponse<Guid>(entity.Id);
        }

        /// <summary>
        ///     任务取消
        /// </summary>
        /// <param name="taskId"></param>
        /// <returns></returns>
        public async Task<AjaxResponse> Cancel(Guid taskId)
        {
            var entity = await _repository
                .Include(t => t.Service)
                .FirstOrDefaultAsync(t => t.Id == taskId);
            if (entity == null)
            {
                throw new UserFriendlyException("The entity is not found!");
            }

            if (entity.Status == TaskStatusEnum.Cancel)
            {
                throw new UserFriendlyException("the task has cancelled!");
            }

            var data = new RequestMessageData
            {
                TaskId = entity.Id,
                Url = entity.Service.Url,
                Parameters = JsonConvert.SerializeObject(entity.Request)
            };

            var count = await _redisHelper.LRemAsync(AppConsts.TaskQueueKey, data);
            return count == 0 ? throw new UserFriendlyException("the task failed cancel!") : new AjaxResponse();
        }

        /// <summary>
        ///     分页获取
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<AjaxResponse<PagedResultDto<TaskDto>>> GetPaged(PagedTaskRequestDto input)
        {
            var query = await _repository.GetQueryableAsync();
            if (input.CreatorId.HasValue)
            {
                query = query.Where(t => t.CreatorId == input.CreatorId);
            }

            if (input.Status.HasValue)
            {
                query = query.Where(t => t.Status == input.Status);
            }

            var list = await query.AsNoTracking()
                .OrderBy(input.Sorting)
                .PageBy(input)
                .ToListAsync();
            var dtoList = ObjectMapper.Map<List<TaskEntity>, List<TaskDto>>(list);
            return new AjaxResponse<PagedResultDto<TaskDto>>(new PagedResultDto<TaskDto>(query.LongCount(), dtoList));
        }


        /// <summary>
        ///     任务重试
        /// </summary>
        /// <param name="taskId"></param>
        /// <returns></returns>
        public async Task<AjaxResponse> Retry(Guid taskId)
        {
            var entity = await _repository
                .Include(t => t.Service)
                .FirstOrDefaultAsync(t => t.Id == taskId);
            if (entity == null)
            {
                return new AjaxResponse($"task id {taskId} is not found!");
            }

            entity.Status = TaskStatusEnum.InQueue;
            var request = new RequestMessageData
            {
                TaskId = entity.Id,
                Url = entity.Service.Url,
                Parameters = JsonConvert.DeserializeObject(entity.Request)
            };
            await _repository.UpdateAsync(entity);
            await _redisHelper.PushAsync(AppConsts.TaskQueueKey, request);
            return new AjaxResponse();
        }
    }
}